/* ----------------------------------------------------------------------------
 * Copyright (c) Huawei Technologies Co., Ltd. 2013-2019. All rights reserved.
 * Description: LiteOS Mem Record Implementation
 * Author: shaodongdong
 * Create: 2013-01-01
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 * 1. Redistributions of source code must retain the above copyright notice, this list of
 * conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice, this list
 * of conditions and the following disclaimer in the documentation and/or other materials
 * provided with the distribution.
 * 3. Neither the name of the copyright holder nor the names of its contributors may be used
 * to endorse or promote products derived from this software without specific prior written
 * permission.
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * --------------------------------------------------------------------------- */
/* ----------------------------------------------------------------------------
 * Notice of Export Control Law
 * ===============================================
 * Huawei LiteOS may be subject to applicable export control laws and regulations, which might
 * include those applicable to Huawei LiteOS of U.S. and the country in which you are located.
 * Import, export and usage of Huawei LiteOS in any manner by you shall be in compliance with such
 * applicable export control laws and regulations.
 * --------------------------------------------------------------------------- */
#include "los_binarytree_pri.h"
#include "stdio.h"
#include "los_memory.h"
#include "los_event.h"
#include "los_exc.h"
#include "los_task_pri.h"
#include "los_memrecord_pri.h"

enum MemActType {
    OS_MEM_VALIDFREE,
    OS_MEM_INVALIDFREE,
    OS_MEM_ALLOC,
};

MemRecordInfo g_memRecord1[RECORD_LEN] = { 0 };
MemRecordInfo g_memRecord2[RECORD_LEN] = { 0 };
MemRecordInfo *g_saveMemRecord = g_memRecord1;
MemRecordInfo *g_printMemRecord = NULL;
MemRecordInfo *g_curPtr = NULL;

volatile INT32 g_memRecordIndex = -1;
volatile UINT32 g_memLastEndIndex = 0;
EVENT_CB_S g_memshowEvent;

extern LosTask g_losTask;
extern UINT64 g_tickCount;
UINT32 g_memRecordShowEnable = 1;
UINT32 g_lastAddrNodeIndex = 0;
UINT32 g_lastReqSizeNodeIndex = 0;
UINT32 g_lastlinkRegNodeIndex = 0;

STATIC VOID OsMemRecordCompressInfo(VOID)
{
    UINT32 count = 0;
    STATIC CHAR infoStr[20];

    UINT32 currentIndex = g_addrNodeIndex;
    for (count = g_lastAddrNodeIndex; count < currentIndex; count++) {
        OsDecTo64F(g_addrNode[count].leaf.nodeID, infoStr, 3);
        printf("~^%s%x^~\n", infoStr, g_addrNode[count].addr);
    }
    g_lastAddrNodeIndex = currentIndex;

    currentIndex = g_reqSizeNodeIndex;
    for (count = g_lastReqSizeNodeIndex; count < currentIndex; count++) {
        OsDecTo64F(g_reqSizeNode[count].leaf.nodeID, infoStr, 2);
        printf("*^%s%d^*\n", infoStr, g_reqSizeNode[count].reqSize);
    }
    g_lastReqSizeNodeIndex = currentIndex;

    currentIndex = g_linkRegNodeIndex;
    for (count = g_lastlinkRegNodeIndex; count < currentIndex; count++) {
        OsDecTo64F(g_linkRegNode[count].leaf.nodeID, infoStr, 2);
        printf("$^%s%x%x%x^$\n", infoStr, g_linkRegNode[count].linkReg1, g_linkRegNode[count].linkReg2,
               g_linkRegNode[count].linkReg3);
    }
    g_lastlinkRegNodeIndex = currentIndex;

    return;
}

STATIC VOID OsMemRecordShow(VOID)
{
    UINT32 startIndex;
    UINT32 endIndex;
    MemRecordInfo *printMemRecord = (MemRecordInfo *)NULL;
    MemRecordInfo *saveMemRecord = (MemRecordInfo *)NULL;
    STATIC CHAR printStr[32];
    STATIC CHAR nodeValue[7];
    UINT32 index = 0;
MEM_REC_PRINT_RESET:
    printMemRecord = g_printMemRecord;
    saveMemRecord = g_saveMemRecord;
    startIndex = g_memLastEndIndex;
    endIndex = g_memRecordIndex;
    if (g_curPtr == NULL) {
        g_curPtr = g_printMemRecord;
    }

    OsMemRecordCompressInfo();

    if (g_curPtr != NULL) {
        printStr[0] = '~';
        printStr[1] = '!';
        while ((g_curPtr == printMemRecord && startIndex < RECORD_LEN) || (g_curPtr == saveMemRecord &&
                                                                           startIndex <= endIndex)) {
            index = 2;
            OsDecTo64F(startIndex, nodeValue, 2);
            printStr[index++] = nodeValue[0];
            printStr[index++] = nodeValue[1];

            OsDecTo64F(g_curPtr[startIndex].addrID, nodeValue, 3);
            printStr[index++] = nodeValue[0];
            printStr[index++] = nodeValue[1];
            printStr[index++] = nodeValue[2];

            OsDecTo64F(g_curPtr[startIndex].reqSizeID, nodeValue, 2);
            printStr[index++] = nodeValue[0];
            printStr[index++] = nodeValue[1];

            OsDecTo64F(g_curPtr[startIndex].actType, nodeValue, 4);
            printStr[index++] = nodeValue[0];
            printStr[index++] = nodeValue[1];
            printStr[index++] = nodeValue[2];
            printStr[index++] = nodeValue[3];

            OsDecTo64F(g_curPtr[startIndex].taskID, nodeValue, 2);
            printStr[index++] = nodeValue[0];
            printStr[index++] = nodeValue[1];

            OsDecTo64F(g_curPtr[startIndex].sysTick, nodeValue, 6);
            printStr[index++] = nodeValue[0];
            printStr[index++] = nodeValue[1];
            printStr[index++] = nodeValue[2];
            printStr[index++] = nodeValue[3];
            printStr[index++] = nodeValue[4];
            printStr[index++] = nodeValue[5];

            OsDecTo64F(g_curPtr[startIndex].linkRegID, nodeValue, 2);
            printStr[index++] = nodeValue[0];
            printStr[index++] = nodeValue[1];

            printStr[index++] = '!';
            printStr[index++] = '~';
            printStr[index++] = '\0';

            printf("%s\n", printStr);
            startIndex++;
        };
    }
    if (startIndex == RECORD_LEN || g_curPtr == NULL) {
        startIndex = 0;
        if (g_curPtr == g_saveMemRecord) {
            printf("Rec:error:the printf speed is low,Rnd\n");
            g_memLastEndIndex = 0;
            goto MEM_REC_PRINT_RESET;
        }
        g_curPtr = g_saveMemRecord;
        printStr[0] = '~';
        printStr[1] = '!';
        while ((startIndex < (endIndex + 1))) {
            index = 2;
            OsDecTo64F(startIndex, nodeValue, 2);
            printStr[index++] = nodeValue[0];
            printStr[index++] = nodeValue[1];

            OsDecTo64F(g_curPtr[startIndex].addrID, nodeValue, 3);
            printStr[index++] = nodeValue[0];
            printStr[index++] = nodeValue[1];
            printStr[index++] = nodeValue[2];

            OsDecTo64F(g_curPtr[startIndex].reqSizeID, nodeValue, 2);
            printStr[index++] = nodeValue[0];
            printStr[index++] = nodeValue[1];

            OsDecTo64F(g_curPtr[startIndex].actType, nodeValue, 4);
            printStr[index++] = nodeValue[0];
            printStr[index++] = nodeValue[1];
            printStr[index++] = nodeValue[2];
            printStr[index++] = nodeValue[3];

            OsDecTo64F(g_curPtr[startIndex].taskID, nodeValue, 2);
            printStr[index++] = nodeValue[0];
            printStr[index++] = nodeValue[1];

            OsDecTo64F(g_curPtr[startIndex].sysTick, nodeValue, 6);
            printStr[index++] = nodeValue[0];
            printStr[index++] = nodeValue[1];
            printStr[index++] = nodeValue[2];
            printStr[index++] = nodeValue[3];
            printStr[index++] = nodeValue[4];
            printStr[index++] = nodeValue[5];

            OsDecTo64F(g_curPtr[startIndex].linkRegID, nodeValue, 2);
            printStr[index++] = nodeValue[0];
            printStr[index++] = nodeValue[1];

            printStr[index++] = '!';
            printStr[index++] = '~';
            printStr[index++] = '\0';

            printf("%s\n", printStr);

            startIndex++;
        };
    }
    g_memLastEndIndex = endIndex + 1;
}

STATIC VOID OsMemRecordExchange(VOID)
{
    if (g_saveMemRecord == g_memRecord1) {
        g_saveMemRecord = g_memRecord2;
        g_printMemRecord = g_memRecord1;
    } else {
        g_saveMemRecord = g_memRecord1;
        g_printMemRecord = g_memRecord2;
    }
    g_memRecordIndex = -1;
    (VOID)LOS_EventWrite(&g_memshowEvent, 0x112);
}

STATIC INLINE VOID OsMemRecordLR(LinkRegNode *linkRegNode)
{
    AARCHPTR framePtr, framePtrTmp;
    UINT32 index = 0;

    linkRegNode->linkReg1 = 0;
    linkRegNode->linkReg2 = 0;
    linkRegNode->linkReg3 = 0;

    framePtr = Get_Fp();
    while (framePtr > OS_SYS_FUNC_ADDR_START && framePtr < OS_SYS_FUNC_ADDR_END && (framePtr % sizeof(char *)) == 0) {
        framePtrTmp = framePtr;
        if (index == 1) {
            linkRegNode->linkReg1 = *((AARCHPTR *)(framePtrTmp));
        } else if (index == 2) {
            linkRegNode->linkReg2 = *((AARCHPTR *)(framePtrTmp));
        } else if (index == 3) {
            linkRegNode->linkReg3 = *((AARCHPTR *)(framePtrTmp));
        }
        framePtr = *((AARCHPTR *)(framePtrTmp - sizeof(AARCHPTR *)));
        index++;
        if (index == 4) {
            break;
        }
    }
}

STATIC VOID OsMemRecordTaskID(VOID)
{
    if (g_losTask.runTask) {
        g_saveMemRecord[g_memRecordIndex].taskID = LOS_CurTaskIDGet();
    } else {
        g_saveMemRecord[g_memRecordIndex].taskID = 0;
    }
}

STATIC INLINE VOID OsMemRecord(VOID *ptr, UINT32 size)
{
    UINT64 tickCount = g_tickCount;
    INT32 nodeID;
    LinkRegNode linkRegNode;
    AddrNode addrNode;
    ReqSizeNode reqSizeNode;

    if (g_memRecordIndex == (RECORD_LEN - 1)) {
        OsMemRecordExchange();
    }
    g_memRecordIndex++;

    OsMemRecordLR(&linkRegNode);
    nodeID = OsBinTreeInsert(&linkRegNode, sizeof(linkRegNode), (BinNode **)&g_linkRegRoot, OsGetLRBinNode,
                             OsCompareLRNode);
    g_saveMemRecord[g_memRecordIndex].linkRegID = nodeID;

    addrNode.addr = (AARCHPTR)ptr;
    nodeID = OsBinTreeInsert(&addrNode, sizeof(AddrNode), (BinNode **)&g_addrRoot, OsGetAddrBinNode,
                             OsCompareAddrNode);
    g_saveMemRecord[g_memRecordIndex].addrID = nodeID;

    g_saveMemRecord[g_memRecordIndex].sysTick = tickCount;

    OsMemRecordTaskID();

    reqSizeNode.reqSize = size;
    nodeID = OsBinTreeInsert(&reqSizeNode, sizeof(ReqSizeNode), (BinNode **)&g_reqSizeRoot, OsGetReqSizeBinNode,
                             OsCompareReqSizeNode);
    g_saveMemRecord[g_memRecordIndex].reqSizeID = nodeID;
}

VOID OsMemRecordMalloc(VOID *ptr, UINT32 size)
{
    if (0 == g_memRecordShowEnable) {
        return;
    }
    OsMemRecord(ptr, size);
    g_saveMemRecord[g_memRecordIndex].actType = OS_MEM_ALLOC;
}

VOID OsMemRecordFree(VOID *ptr, UINT32 size)
{
    INT32 actType;
    if (0 == g_memRecordShowEnable) {
        return;
    }
    actType = (size == 0) ? OS_MEM_INVALIDFREE : OS_MEM_VALIDFREE;
    OsMemRecord(ptr, size);
    g_saveMemRecord[g_memRecordIndex].actType = actType;
}

VOID OsMemRecordShowTask(VOID)
{
    (VOID)LOS_EventInit(&g_memshowEvent);
    while (1) {
        (VOID)LOS_EventRead(&g_memshowEvent, 0xFFF, LOS_WAITMODE_OR | LOS_WAITMODE_CLR, MEM_RECORDSHOW_TIMEOUT);
        if (g_memRecordShowEnable) {
            OsMemRecordShow();
        }
    }
}

